/*--------------------------------------------------------------------------------
 * Adaptive Intelligence - Foundation Framework Class Library
 * Version 2.00
 * 
 * Character Buffer Class
 *      Provides a class template.
 *------------------------------------------------------------------------------*/

/*--------------------------------------------------------------------------------
 * System Imports
 *------------------------------------------------------------------------------*/
using System;
using System.Text;

namespace Adaptive.Foundation
{
    /// <summary>
    /// Provides a template class.
    /// </summary>
    public class CharacterBuffer : DisposableObjectBase
    {
        /*--------------------------------------------------------------------------------
         * Public Event Definitions
         *------------------------------------------------------------------------------*/
        #region Public Event Definitions
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Constants Declarations
         *------------------------------------------------------------------------------*/
        #region Private Constants Declarations
        /// <summary>
        /// Default capacity.
        /// </summary>
        private const int DEF_CAPACITY = 128;

        #endregion

        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// Size of the buffer.
        /// </summary>
        private int _capacity = DEF_CAPACITY;
        /// <summary>
        /// The actual character buffer.
        /// </summary>
        private char[] _buffer;
        /// <summary>
        /// Index of the first character.
        /// </summary>
        private int _headIndex;
        /// <summary>
        /// Index of the last character.
        /// </summary>
        private int _tailIndex;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose / Destructor Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose / Destructor Methods
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public CharacterBuffer()
            : base()
        {
            _buffer = new char[_capacity];
        }
        /// <summary>
        /// Initializes a new instance of the class to the specified size.
        /// </summary>
        /// <param name="capacity">
        /// The initial capacity of the buffer, in bytes.
        /// </param>
        public CharacterBuffer(int capacity)
        {
            _capacity = capacity;
            _buffer = new char[capacity];
        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating whether the object is being disposed.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            if ((!Disposed) && (disposing))
            {
                _buffer = null;
            }

            base.Dispose(disposing);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets the capacity of this character buffer.
        /// </summary>
        /// <value>
        /// The maximum size of the buffer.
        /// </value>
        public int Capacity
        {
            get { return (_capacity); }
        }
        /// <summary>
        /// Gets or sets the length of the buffer.
        /// </summary>
        /// <value>
        /// The size of the buffer.
        /// </value>
        public int Length
        {
            get
            {
                return _tailIndex - _headIndex;
            }
            set
            {
                _tailIndex = _headIndex + value;
                if (_tailIndex >= _capacity)
                    throw new IndexOutOfRangeException("Tail index greater than capacity");
            }
        }
        /// <summary>
        /// Gets or sets the value of the character at the specified index.
        /// </summary>
        /// <value>
        /// The character value at the specified index.
        /// </value>
        public char this[int index]
        {
            get { return (_buffer[index + _headIndex]); }
            set { _buffer[index + _headIndex] = value; }
        }

        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Appends a character to the buffer.
        /// </summary>
        /// <param name="charToAppend">
        /// The character to be appended.
        /// </param>
        public void Append(char charToAppend)
        {
            if (_tailIndex >= _capacity)
               IncreaseCapacity(Length + 1);

            _buffer[_tailIndex++] = charToAppend;
        }
        /// <summary>
        /// Appends a string to the buffer.
        /// </summary>
        /// <param name="stringToAppend">The string value to be appended.</param>
        public void Append(string stringToAppend)
        {
            int count = 0;
            int length = 0;

            length = stringToAppend.Length;

            if (length + _tailIndex >= _capacity)
                IncreaseCapacity(Length + length);

            for (count = 0; count < length; count++)
                _buffer[_tailIndex++] = stringToAppend[count];
        }
        /// <summary>
        /// Appends the contents of another <b>CharacterBuffer</b> to the current instance.
        /// </summary>
        /// <param name="bufferToCopy">The <see cref="CharacterBuffer"/> whose contents are to be appended.</param>
        public void Append(CharacterBuffer bufferToCopy)
        {
            int count = 0;
            int length = 0;

            length = bufferToCopy.Length;
            if (length + _tailIndex >= _capacity)
                IncreaseCapacity(Length + length);

            for (count = 0; count < length; count++)
                _buffer[_tailIndex++] = bufferToCopy[count];
        }
        /// <summary>
        /// Clears the contents of the object instance without disposing of the instance.
        /// </summary>
        public void Clear()
        {
            int count = 0;
            int length = 0;

            length = _tailIndex;
            for (count = _headIndex; count <= length; count++)
                _buffer[count] = (char)0;

            _headIndex = 0;
            _tailIndex = 0;

        }
        /// <summary>
        /// Copies the supplied buffer to the current character buffer.
        /// </summary>
        /// <param name="bufferToCopy">The character array to be copied.</param>
        /// <param name="startIndex">The index at which to begin copying.</param>
        /// <param name="bufferLength">The number of characters to copy.</param>
        public void SetBuffer(char[] bufferToCopy, int startIndex, int bufferLength)
        {
            int count = 0;
            int length = startIndex + bufferLength;

            _capacity = length;
            _buffer = new char[bufferLength];
            for (count = startIndex; count < (length); count++)
                _buffer[count - startIndex] = bufferToCopy[count];
            _tailIndex = length;
            _headIndex = 0;
        }
        /// <summary>
        /// Remove a character at the specified index from the buffer.
        /// </summary>
        /// <param name="index">The index of the character to remove.</param>
        public void Remove(int index)
        {
            Remove(index, 0);
        }
        /// <summary>
        /// Remove a specified number of characters starting at the specified index.
        /// </summary>
        /// <param name="startingIndex">The starting index of the characters to remove.</param>
        /// <param name="numCharacters">The number of characters to remove.</param>
        public void Remove(int startingIndex, int numCharacters)
        {
            numCharacters = Math.Min(numCharacters, Length);
            if (startingIndex == 0)
            {
                _headIndex += numCharacters;
            }
            else
            {
                Array.Copy(_buffer, startingIndex + _headIndex + numCharacters, 
                                   _buffer, startingIndex + _headIndex,
                                    _tailIndex - (startingIndex + _headIndex + numCharacters));
            }
        }
        /// <summary>
        /// Find the first instance of a character in the buffer, and
        /// return its index.  This returns -1 if the character is
        /// not found.
        /// </summary>
        /// <param name="characterToFind">The character to find.</param>
        /// <returns>The index of the specified character, or -1
        /// for not found.</returns>
        public int IndexOf(char characterToFind)
        {
            int count = 0;

            for (count = _headIndex; count < _tailIndex; count++)
            {
                if (_buffer[count] == characterToFind) return (count - _headIndex);
            }
            return (-1);
        }
        /// <summary>
        /// Return the current contents as a string.
        /// </summary>
        /// <returns>The new string.</returns>
        public override String ToString()
        {
            return (new String(_buffer, _headIndex, _tailIndex - _headIndex));
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Private Methods / Functions
        /// <summary>
        /// Increases the current storage capacity.
        /// </summary>
        /// <param name="increaseBy">
        /// The number of bytes to be added to the current buffer.
        /// </param>
        private void IncreaseCapacity(int increaseBy)
        {
            int size = Length + increaseBy;

            if (size > _capacity)
            {
                if ((size > (_capacity >> 1)) && (Length < _capacity - 1))
                    Compact();
                else
                    ResizeArray(increaseBy);
            }
        }
        /// <summary>
        /// Shifts the internal byte contents to the start of the buffer array.
        /// </summary>
        private void Compact()
        {
            int length = Length;
            int count = 0;

            for (count = 0; count < length; count++)
            {
                _buffer[count] = _buffer[count + _headIndex];
            }
            _headIndex = 0;
            _tailIndex = length;
        }
        /// <summary>
        /// Reallocates the buffer to be larger. For the new size, this
        /// uses the max of the requested length and double the current
        /// capacity.
        /// This does not shift, meaning it does not change the head or
        /// tail indices.
        /// </summary>
        /// <param name="requestedLength">The new requested length.</param>
        private void ResizeArray(int requestedLength)
        {
            char[] newBuffer = null;
            int newSize = 0;

            //Calculate the new size.
            newSize = Math.Max(_capacity * 2, requestedLength);
            newSize = Math.Max(newSize, 16);
            newBuffer = new char[newSize];

            Array.Copy(_buffer, 0, newBuffer, 0, _capacity);
            _buffer = newBuffer;
            _capacity = newSize;
        }
        #endregion

    }
}
